import org.codehaus.groovy.grails.plugins.authorize.user.AuthUserStatus;

class LoginController {

    AuthEmailService authEmailService

    def index = {
        def user = new AuthUser()
        if (session.authenticate) {
            flash.message = "authorize.user.authenticate"
            session.authenticate = null
        }
        if (session.unauthorized) {
            user.generateErrors()
            user.errors.reject("authorize.user.unauthorized", "You do not have the permissions to perform that action")
            session.unauthorized = null
        }
        render(view: "login", model: [user:user])
    }

    def login = {LoginValidationCommand cmd ->
        if (cmd.hasErrors()) {
            def user = new AuthUser()
            user.errors = cmd.errors
            return [user: user]
        }

        def user = AuthUser.findByUsername(params.username)
        String calcPassword = params.password.encodeAsPassword()
        if (user == null) {
            user = new AuthUser()
            user.generateErrors()
            log.warn("User not found: ${params.username}")
            user.errors.reject("authorize.login.username.not.found", "The username you entered does not exist")
            return [user: user]
        }

        if (user.passwordEnc != calcPassword) {
            user.generateErrors()
            log.info("Password mismatch for ${params.username}")
            user.errors.reject("authorize.login.password.invalid", "The password you entered is invalid")
            return [user: user]
        }

        // The user account is still pending
        if (user.status != AuthUserStatus.ACTIVE) {
            user.generateErrors()
            if (user.status == AuthUserStatus.PENDING) {
                user.errors.reject("authorize.account.pending", "This account is still pending confirmation")
                return [user: user];
            }

            // The user account is suspended and cannot be used
            // TODO: Maybe forward to a view that gives them a way to 'unsuspend' themselves
            if (user.status == AuthUserStatus.SUSPENDED) {
                user.errors.reject("authorize.account.suspended", "This account has been suspended")
                return [user: user];
            }

            // The user account is inactive - this means it hasn't been used in a long time and was
            // therefore adjusted in status.
            // TODO: Maybe forward to a view that gives them a way to re-activate their account
            if (user.status == AuthUserStatus.INACTIVE) {
                user.errors.reject("authorize.account.not.active", "This account is now dormant")
                return [user: user];
            }
        }

        // Add the AuthUser object as a 'session.authorized' object in the session
        session.authenticated = user
        // Update the last accessed date, for auditing
        user.lastAccess = new Date()
        if (!user.save()) {
            for (String error in user.errors) {
                log.error(error);
            }
        }
        flash.message = "authorize.login.welcome"
        flash.args = ["${user.firstname}"]

        // If there were original request parameters then forward to those parameters
        if (session.originalRequestParams) {
            redirect(controller: session.originalRequestParams.controller, action: session.originalRequestParams.action, params: session.originalRequestParams)
            session.originalRequestParams = null
            return;
        }

        // If a success controller and action were configured, then chain the action to the configured action
        def loginSuccessController = grailsApplication.config?.authorize?.login?.success?.controller
        def loginSuccessAction = grailsApplication.config?.authorize?.login?.success?.action
        if (loginSuccessController && loginSuccessAction) {
            // Very important to remove the login details for this user so it's not visible in the subsequent view
            params.remove("username")
            params.remove("password")
            chain(controller: loginSuccessController, action: loginSuccessAction)
            return
        }

        // When all else fails, redirect to the home page
        redirect(uri: "/")
    }

    def logout = {
        session.authenticated = null
        session.account = null
        flash.message = "authorize.login.logout"
        redirect(uri: "/")
    }

    def forgotPassword = {

    }

    def doForgotPassword = {
        AuthUser user = AuthUser.findByEmail(params.email)
        if (!user) {
            user.generateErrors();
            user.errors.reject("authorize.forgotpassword.email.unknown", "The email address you entered was not found on our system")
            render(view: 'forgotPassword')
            return;
        }

        // Calculate another cryptic password using Glen Smiths technique
        def PW_POOL = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
        def genPw = ""
        8.times {
            genPw += PW_POOL[new Random().nextInt(PW_POOL.size() - 1)]
        }
        user.passwordEnc = genPw.encodeAsPassword()

        // Send an email to the user with their new password
        AuthUser.withTransaction {status ->
            try {
                if (!user.save()) {
                    flash.message = "authorize.forgotpassword.not.saved"
                    render(view: "forgotPassword")
                    return;
                }

                String fromAddr = grailsApplication.config.authorize?.smtp?.from
                String emailContent = g.render(template: "/login/emails/forgotPassword", model: [user: user, newPassword: genPw])
                authEmailService.sendMail(fromAddr, user.email, "Your password has been reset", emailContent)
                flash.message = "authorize.forgotpassword.email.sent"
            } catch (Exception e) {
                status.setRollbackOnly()
                flash.message = "authorize.forgotpassword.error.sending.email"
            }
        }
        redirect(controller: 'login')
    }

}

class LoginValidationCommand {
    String username
    String password

    static constraints = {
        username(nullable: false, blank: false)
        password(nullable: false, blank: false)
    }
}